package org.groupg.project.configmanager.repository;


import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import org.groupg.project.configmanager.util.JPAUtil;

import java.util.List;
import java.util.Optional;

public abstract class BaseRepository<T, ID> {
    private final Class<T> entityClass;

    protected BaseRepository(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public List<T> findAll() {
        return JPAUtil.executeInTransaction(entityManager -> {
            CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            CriteriaQuery<T> cq = cb.createQuery(entityClass);
            Root<T> root = cq.from(entityClass);
            cq.select(root);
            TypedQuery<T> query = entityManager.createQuery(cq);
            return query.getResultList();
        });
    }

    public Optional<T> findById(ID id) {
        return JPAUtil.executeInTransaction(entityManager -> {
            T entity = entityManager.find(entityClass, id);
            return Optional.ofNullable(entity);
        });
    }

    public T save(T entity) {
        return JPAUtil.executeInTransaction(entityManager -> {
            if (isNew(entity)) {
                entityManager.persist(entity);
                return entity;
            } else {
                return entityManager.merge(entity);
            }
        });
    }

    public void delete(ID id) {
        JPAUtil.executeInTransaction(entityManager -> {
            T entity = entityManager.find(entityClass, id);
            if (entity != null) {
                entityManager.remove(entity);
            }
        });
    }

    public boolean existsByName(String name) {
        return JPAUtil.executeInTransaction(entityManager -> {
            String jpql = String.format("SELECT COUNT(e) FROM %s e WHERE e.name = :name",
                                      entityClass.getSimpleName());
            Long count = entityManager.createQuery(jpql, Long.class)
                                    .setParameter("name", name)
                                    .getSingleResult();
            return count > 0;
        });
    }

    public boolean existsByNameExcludingId(String name, ID id) {
        return JPAUtil.executeInTransaction(entityManager -> {
            String jpql = String.format("SELECT COUNT(e) FROM %s e WHERE e.name = :name AND e.id != :id",
                                      entityClass.getSimpleName());
            Long count = entityManager.createQuery(jpql, Long.class)
                                    .setParameter("name", name)
                                    .setParameter("id", id)
                                    .getSingleResult();
            return count > 0;
        });
    }

    protected abstract boolean isNew(T entity);
}